Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ZFS encryption #467

Open
wants to merge 20 commits into
base: master
Choose a base branch
from

Conversation

lowjoel
Copy link

@lowjoel lowjoel commented Jun 12, 2024

Supersedes #373. Description copied:

Why

As I stated a while ago in #218, I would like clevis to be able to unlock ZFS datasets that have native encryption enabled. This is my attempt at adding this by storing the data in zfs properties.

How

This is achieved by storing the clevis data (output of clevis-encrypt) in ZFS User Properties. From zfsprops(8):

   User Properties
     In addition to the standard native properties, ZFS supports arbitrary user properties.  User
     properties have no effect on ZFS behavior, but applications or administrators can use them
     to annotate datasets (file systems, volumes, and snapshots).

     User property names must contain a colon (":") character to distinguish them from native
     properties.  They may contain lowercase letters, numbers, and the following punctuation
     characters: colon (":"), dash ("-"), period ("."), and underscore ("_").  The expected
     convention is that the property name is divided into two portions such as module:property,
     but this namespace is not enforced by ZFS.  User property names can be at most 256
     characters, and cannot begin with a dash ("-").

     When making programmatic use of user properties, it is strongly suggested to use a reversed
     DNS domain name for the module component of property names to reduce the chance that two
     independently-developed packages use the same property name for different purposes.

     The values of user properties are arbitrary strings, are always inherited, and are never
     validated.  All of the commands that operate on properties (zfs list, zfs get, zfs set, and
     so forth) can be used to manipulate both native properties and user properties.  Use the zfs
     inherit command to clear a user property.  If the property is not defined in any parent
     dataset, it is removed entirely.  Property values are limited to 8192 bytes.

Properties

All clevis user properties are prefixed with latchset.clevis

* one property to check if a dataset is bound: `latchset.clevis:labels`, should be a space-separated list of bound labels.  or absent when unbound.

* one or more properties to store the clevis data: `latchset.clevis.label:LABEL_NAME[-N]` where `-N` is an integer suffix when the data for label LABEL_NAME is too large for one property.

If there are more than 10 properties needed, the integer will be 0-padded to help with sorting to easily combine them when unlocking.

As noted above (at the end), the limit of the value of a user property is 8192 bytes. A simple 1-host tang setup will probably not go over this limit, but with a more complicated setup with clevis-encrypt-sss, it is possible.

Because of that, the clevis data is split in 8k chunks, and saved in multiple user-properties. These are combined upon unlock.

"Works": (works on my machine, needs more testing)

* binding ZFS dataset with `clevis-zfs-bind`

* unbinding ZFS dataset with `clevis-zfs-unbind`

* testing and unlocking ZFS dataset with `clevis-zfs-unlock`

* splitting and combining zfs-properties (tested with a limit of 800 instead of 8000)

To Do:

* [ ]  manpages

* [ ]  initramfs hooks

* [ ]  rebinding support? (like clevis-luks-rebind)

* [x]  Maybe: multiple "slots" support. Currently only one "slot" is available. Added label support

* [ ]  clean up commits if this is not squashed

Further work by @lowjoel:

  • Cleaned up the error messages, fixed lints reported by shellcheck
  • Completed support for initramfs. This requires OpenZFS 2.2+ because of openzfs/zfs@6e01593. Dracut has not been tested after my changes.
  • Did a full clevis-zfs-bind, clevis-zfs-unlock (at reboot), and clevis-zfs-unbind
  • PPA available for Ubuntu 22.04
  • Integrate clevis-zfs-test with Meson

Once we're happy with the code I can squash the commits to those by @techhazard and myself.

@lowjoel lowjoel mentioned this pull request Jun 12, 2024
5 tasks
@lowjoel lowjoel force-pushed the add-zfs-support branch 5 times, most recently from 59a2544 to 97bf779 Compare June 14, 2024 14:15
@almereyda
Copy link

Would there be any instructions, how this can be tested?

I'd like to aid in this implementation, but am unsure how.

Is there something that can be done by third-parties?

@lowjoel
Copy link
Author

lowjoel commented Jul 1, 2024

Would there be any instructions, how this can be tested?

I've packaged my changes for Ubuntu in a PPA. After you install that, it's essentially just using clevis-zfs-bind with the same Clevis configuration parameters. The package already triggers initramfs to be regenerated automatically.

I'd like to aid in this implementation, but am unsure how.

Testing would be good. But I only have this specific initramfs configuration.

@almereyda
Copy link

almereyda commented Jul 2, 2024

Thank you for the quick reply. I have found the PPA. Unfortunately I have never used Clevis and cannot unpack the sentence

just using clevis-zfs-bind with the same Clevis configuration parameters

to infer what a test setup would look like.

I'm able to set up an LXD/Incus virtual network that contains an Ubuntu VM/system container and a block devices that contains an encrypted ZFS pool plus a separate instance for Clevis.

As a tester I would ask myself:

  • Which are the steps to set up the Clevis server to provide valid ZFS decryption keys?
  • Which are the steps to set up the Ubuntu client to pull the encryption keys in initramfs?

I am having the intuition that bind, unlock and unbind are the hooks that make this work, but I am left asking myself how.

Would it seem suitable to add some documentation with this cycle? The information could go to the clevis.1.adoc man page, a new one like clevis-zfs.1.doc or the existing clevis-decrypt.1.adoc, plus into the README.md or INSTALL.md. This would allow to assure that the presence of the feature is also reflected in documentation.

@lowjoel
Copy link
Author

lowjoel commented Jul 2, 2024

to infer what a test setup would look like.

Ah, OK. Have a look at the Arch wiki. In my case, I want to bind the key with both my TPM and with a Yubikey, so I used clevis-zfs-bind -d rpool -l boot sss '{"t": 2, "pins": {"tpm2": {"pcr_ids": "0"}, "yubikey": {"challenge_size": 64, "salt_size": 64}}}'.

* Which are the steps to set up the Clevis server to provide valid ZFS decryption keys?

If you want a server, I think you're referring to Tang. I haven't set that up; my key unlock is purely held on the TPM and with a Yubikey, there's no network involved in my setup.

* Which are the steps to set up the Ubuntu client to pull the encryption keys in initramfs?

I am having the intuition that bind, unlock and unbind are the hooks that make this work, but I am left asking myself how.

Essentially Clevis does most of the heavy lifting. All this PR does is teach Clevis how to unlock ZFS datasets, which is why there isn't significant documentation. The two arguments:

  • -d tells Clevis which dataset you want to unlock. In my case, rpool (ZFS on Root)
  • -l tells Clevis where to store the actual ciphertext containing the key to unlock the ZFS dataset. I used the label boot which you can verify using zfs get latchset.clevis.label:boot rpool

clevis-zfs-unlock is called at initramfs to essentially scan for labels and try unlocking the datasets. clevis-zfs-unbind just removes the labels from the dataset.

Would it seem suitable to add some documentation with this cycle? The information could go to the clevis.1.adoc man page, a new one like clevis-zfs.1.doc or the existing clevis-decrypt.1.adoc, plus into the README.md or INSTALL.md. This would allow to assure that the presence of the feature is also reflected in documentation.

Calling clevis-zfs-bind --help also prints that documentation but I should figure that out. I'd like some confirmation that it works for someone other than me before I go write it 😅

@almereyda
Copy link

I'm on it, but give a few months. Thanks for the link and for helping me understand the relationship between Tang and Clevis better.

Installing the ZFS integration should not imply installing the LUKS
integration.
@r4nc0r
Copy link

r4nc0r commented Oct 22, 2024

this would be a huge help for our organization 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants